const path = require('path');
const fs = require('fs');
const util = require('util');
const http = require('http');
const mime = require('mime');
const ejs = require('ejs');

const favicon = path.join(__dirname, '../public/favicon.ico');
const template = path.join(__dirname, '../template/index.html');
const render = util.promisify(ejs.renderFile);

class Service {
	constructor (options = {}) {
		this.port = options.port || 3000;
		this.route = '/';
		this.dir = options.dir || __dirname;
	}

	start () {
		const app = http.createServer(this.callBack.bind(this));

		app.listen(3600, () => {
			console.log('server is running...');
		});
	}
	
	routeCreate (route) {
		return path.join(this.dir, route);
	}

	async callBack (req, res) {
		this.route = req.url;
		let route = this.routeCreate(req.url);

		const type = mime.getType(path.extname(route));

		const traversal = util.promisify(this.traversal.bind(this));
		if (route.includes('favicon.ico')) route = favicon;
		try {
			const stream = await traversal(route);
			res.setHeader('Content-type', `${type}; charset=utf-8`);
			if (typeof stream == 'string') {
				res.end(stream);
			} else {
				stream.pipe(res);
			}
		} catch (error) {
			this.withTheException(error, res);
		}
	}

	traversal (dir, callback) {
		try {
			const state = fs.statSync(dir);
			if (state.isDirectory()) {
				this.dirSolve(dir, callback);
			} else {
				this.fileSolve(dir, callback);
			}
		} catch (error) {
			callback(error, null);
		}
	}

	async dirSolve (filename, callback) {
		try {
			const files = fs.readdirSync(filename);
			const data = this.renderCompose(files, filename);
			const context = await render(template, data);
			callback(null, context);
		} catch (error) {
			callback(error, null);
		}
	}

	renderCompose (list, filename) {
		const route = this.route === '/'
		 ? null : path.dirname(this.route);
		return {
			files: list.map(file => ({
				name: file,
				path: path.join(this.route, file)
			})),
			root: route,
			title: path.basename(filename),
		}
	}

	fileSolve (filename, callback) {
		try {
			const readStream = fs.createReadStream(filename);
			callback(null, readStream);
		} catch (error) {
			callback(error, null);
		}
	}

	withTheException (error, res) {
		res.setHeader('Content-type', 'text/html; charset=utf-8');
		res.end('Not Found');
		console.log(error);
	}
}

module.exports = Service;